package com.talkyun.utils.download;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;

import static java.net.HttpURLConnection.HTTP_PARTIAL;

class DownTask extends Thread {
    private static final Logger logger = LoggerFactory.getLogger(DownTask.class);
    private static final int MAX_RETRY_TIME = 100;
    private static final int HTTP_TIMEOUT = 3 * 1000;

    private String url;
    private String file;
    private int bgnInx;
    private int endInx;
    private int taskNo;

    DownTask(String url, String file, int bgnInx, int endInx, int taskNo) {
        super.setName("dmx-down-" + taskNo);
        super.setDaemon(true);

        this.url = url;
        this.file = file;
        this.bgnInx = bgnInx;
        this.endInx = endInx;
        this.taskNo = taskNo;
    }

    @Override
    public void run() {
        logger.info("  -> 线程 " + taskNo + " 下载区间: " + bgnInx + "-" + endInx);

        for (int i = 1; i <= MAX_RETRY_TIME; i++) {
            try {
                this.doRun();
                return;
            } catch (IOException e) {
                this.doSleep(5 * 1000);
            }
        }

        logger.error("Down exception! " + url);
        throw new RuntimeException("Download " + url + " exception! " + taskNo);
    }

    private void doRun() throws IOException {
        HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
        conn.setRequestMethod("GET");
        conn.setReadTimeout(HTTP_TIMEOUT);
        conn.setConnectTimeout(HTTP_TIMEOUT);

        // 设置请求数据的区间
        conn.setRequestProperty("Range", "bytes=" + bgnInx + "-" + endInx);

        // 请求部分数据的响应码是 206
        if (conn.getResponseCode() == HTTP_PARTIAL) {
            InputStream ins = conn.getInputStream();
            RandomAccessFile raf = new RandomAccessFile(new File(file), "rwd");
            //  更新文件的写入位置，bgnInx
            raf.seek(bgnInx);
            try {
                streamCopy(ins, raf);
            } finally {
                doClose(ins);
                doClose(raf);
            }
        } else {
            throw new IOException("Response code " + conn.getResponseCode());
        }
    }

    private void streamCopy(InputStream ins, RandomAccessFile out) throws IOException {
        byte[] buf = new byte[1024 * 4];
        int len;
        while ((len = ins.read(buf)) != -1) {
            out.write(buf, 0, len);
            bgnInx += len;
        }
    }

    private void doClose(Closeable cls) {
        try {
            if (cls != null) {
                cls.close();
            }
        } catch (Throwable t) {
            logger.error("Fail to close! ", t);
        }
    }

    private void doSleep(int ms) {
        try {
            Thread.sleep(ms);
        } catch (InterruptedException e) {
            // ignore
        }
    }
}